﻿Imports SharedTools
Imports System.Xml
Imports Org.BouncyCastle.Crypto.Parameters
Imports Org.BouncyCastle.Security
Imports Org.BouncyCastle.X509
Imports Org.BouncyCastle.Ocsp
Imports Org.BouncyCastle.Math
Imports Org.BouncyCastle.Asn1.Ocsp
Imports Org.BouncyCastle.Asn1.X509
Imports Org.BouncyCastle.Asn1

Public Class CRLManager

    Public Shared Function RevokeCertificate(t As CertificateType) As GenericQueryResponse
        Dim p = Repository.CurrentProfile
        Dim id As Integer
        If t = CertificateType.Signature Then
            id = p.SigCert.ID
        Else
            id = p.CryptoCert.ID
        End If
        Dim xmlToSing = XmlTool.GenerateXML("RevocationRequestQuery", "Username", p.UserName, id)


        Dim signedXML = XmlTool.SignXml(DirectCast(p.SigCert.CertificateKey(Repository.CurrentPassword).Private, RsaPrivateCrtKeyParameters), xmlToSing)

        Dim res = My.Application.CAMessageService.SendRevocationRequest(signedXML)

        If XmlTool.CheckSignature(res, DirectCast(Repository.CAcertificate.GetPublicKey, RsaKeyParameters)) Then
            Dim response As GenericQueryResponse = DirectCast([Enum].Parse(GetType(GenericQueryResponse), XmlTool.GetAttributeValue("RevocationRequestResponse", "Response", res)), GenericQueryResponse)
            Select Case response
                Case GenericQueryResponse.Ok
                    If t = CertificateType.Signature Then
                        If p.SigCert IsNot Nothing AndAlso p.SigCert.Certificate IsNot Nothing Then
                            p.OldCertificates.Add(p.SigCert)
                            CertificatesManager.ResetCertificate(CertificateType.Signature, p)
                        End If
                    End If
                    'We always revoke the crypto cert, either only it or both the sig and crypto. In any case, it gets removed.
                    If p.CryptoCert IsNot Nothing AndAlso p.CryptoCert.Certificate IsNot Nothing Then
                        p.OldCertificates.Add(p.CryptoCert)
                        CertificatesManager.ResetCertificate(CertificateType.Cryptography, p)
                    End If

                    ProfileManager.SaveProfile(p)
            End Select

            Return response
        Else
            Throw New SignatureException
        End If
    End Function

    Public Shared Function DownloadCRL() As GenericQueryResponse
        Dim p = Repository.CurrentProfile

        Dim xml = XmlTool.GenerateXML("DownloadCRLQuery", "Username", p.UserName)
        Dim signedXML = XmlTool.SignXml(DirectCast(p.SigCert.CertificateKey(Repository.CurrentPassword).Private, RsaPrivateCrtKeyParameters), xml)

        Dim res = My.Application.CAMessageService.DownloadCRL(signedXML)

        If XmlTool.CheckSignature(res, DirectCast(Repository.CAcertificate.GetPublicKey, RsaKeyParameters)) Then
            Dim response As GenericQueryResponse = DirectCast([Enum].Parse(GetType(GenericQueryResponse), XmlTool.GetAttributeValue("DownloadCRLResponse", "Response", res)), GenericQueryResponse)
            Select Case response
                Case GenericQueryResponse.Ok
                    Dim CRLstring = XmlTool.GetValue("CRL", res)
                    Dim CRLbytes = Convert.FromBase64String(CRLstring)
                    Dim crlParser = New X509CrlParser
                    Dim CRL = crlParser.ReadCrl(CRLbytes)

                    p.LocalCRL = CRL

                    ProfileManager.SaveProfile(p)
            End Select
            Return response
        Else
            Throw New SignatureException
        End If
    End Function

    Public Shared Function CheckCertificateAgainstLocalCRL(cert As X509Certificate) As Boolean
        Dim crl = Repository.CurrentProfile.LocalCRL
        Return crl.IsRevoked(cert)
    End Function

    Public Shared Function CheckCertificateAgainstOCSP(cert As X509Certificate) As Tuple(Of OCSPQueryResponse, DateTime)

        'Generate the id for the certificate we are looking for
        Dim id As New CertificateID(CertificateID.HashSha1, cert, cert.SerialNumber)
        ' Load a new generator for the request
        Dim ocspRequestGenerator As New OcspReqGenerator()
        ' Add the certificate ID to the generator
        ocspRequestGenerator.AddRequest(id)

        Dim rand As New SecureRandom
        Dim nonce As BigInteger = BigInteger.ValueOf(rand.Next)
        'Dim oids As New ArrayList()
        'Dim values As New ArrayList()

        'oids.Add(OcspObjectIdentifiers.PkixOcspNonce)
        'values.Add(New X509Extension(False, New DerOctetString(nonce.ToByteArray())))
        Dim dict As New Dictionary(Of Object, Object)
        dict.Add(OcspObjectIdentifiers.PkixOcspNonce, New X509Extension(False, New DerOctetString(nonce.ToByteArray())))

        ocspRequestGenerator.SetRequestExtensions(New X509Extensions(dict))
        Dim req = ocspRequestGenerator.Generate()

        Dim xml = XmlTool.GenerateXML("OCSPQuery", "Username", Repository.CurrentProfile.UserName, cert.SerialNumber.IntValue)
        xml = XmlTool.AddElement(xml, "OCSPQuery", "OCSPRequest", Convert.ToBase64String(req.GetEncoded))
        Dim signedXml = XmlTool.SignXml(DirectCast(Repository.CurrentProfile.SigCert.CertificateKey(Repository.CurrentPassword).Private, RsaPrivateCrtKeyParameters), xml)

        Dim res = My.Application.CAMessageService.SendOCSPQuery(signedXml)

        If XmlTool.CheckSignature(res, DirectCast(Repository.CAcertificate.GetPublicKey, RsaKeyParameters)) Then
            Dim response As GenericQueryResponse = DirectCast([Enum].Parse(GetType(GenericQueryResponse), XmlTool.GetAttributeValue("OCSPQueryResponse", "Response", res)), GenericQueryResponse)
            Select Case response
                Case GenericQueryResponse.Ok
                    Dim ocsprespBytes = Convert.FromBase64String(XmlTool.GetValue("OCSPResponse", res))

                    'Dim responseTbsBytes = Convert.FromBase64String(XmlTool.GetValue("Tbs", res))
                    'Dim algIdBytes = Convert.FromBase64String(XmlTool.GetValue("Algorithm", res))
                    'Dim responseSignatureBytes = Convert.FromBase64String(XmlTool.GetValue("Signature", res))
                    'Dim responseCertificate = Convert.FromBase64String(XmlTool.GetValue("Certificate", res))

                    'Note: we piggy-back a simple enum response to the OCSP query, because I don't understand how to deserialize correctly the BasicOcspResp object :(
                    Dim ocspresponse As OCSPQueryResponse = DirectCast([Enum].Parse(GetType(OCSPQueryResponse), XmlTool.GetAttributeValue("OCSPQueryResponse", "OCSPResponse", res)), OCSPQueryResponse)

                    If ocspresponse = OCSPQueryResponse.Revoked Then
                        Return New Tuple(Of OCSPQueryResponse, DateTime)(ocspresponse, DateTime.Parse(XmlTool.GetAttributeValue("OCSPQueryResponse", "RevocationDate", res)))
                    Else
                        Return New Tuple(Of OCSPQueryResponse, DateTime)(ocspresponse, Nothing)
                    End If
                Case GenericQueryResponse.ClientUnknownException
                    Return New Tuple(Of OCSPQueryResponse, DateTime)(OCSPQueryResponse.ClientUnknownException, Nothing)
                Case GenericQueryResponse.SignatureException
                    Return New Tuple(Of OCSPQueryResponse, DateTime)(OCSPQueryResponse.SignatureException, Nothing)
            End Select
        Else
            Throw New SignatureException
        End If
    End Function

End Class
